using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

using MG3D.Debugger;

namespace MG3D.DX9
{
    public sealed class DeviceSettings
    {
        public int adapterOrdinal;                 // Retrieves the number of the adapter. Usually is 0 except we detect mutple graphic cards
        public AdapterInformation adapterInfo;     // Retrieves information of the adapter
        public DeviceType deviceType;              // Specifies the adapter type
        public Format adapterFormat;               // Surface format
        public CreateFlags behaviorFlags;          // The fags that are used to create the device. Usualy software, hardware or pure device
        public PresentParameters presentParams;    // Descrives the parameters used to create the device
        public Caps caps;                          // Shows the caracteristics of the device
        public bool isFullScreen;                  // Shows if it is full screen or windowed
        public int width;                          // Width of the backbuffer
        public int height;                         // Height of the backbuffer

        public DeviceSettings(int width, int height, bool isFullScreen)
        {
            this.width = width;
            this.height = height;
            this.isFullScreen = isFullScreen;
            this.deviceType = DeviceType.Reference;	// The slowest

            // Get the ordinal for  the default adapter    
            int adapterOrdinal = Manager.Adapters.Default.Adapter;

            // Trying to get an optimal configuration
            caps = Manager.GetDeviceCaps(adapterOrdinal, DeviceType.Hardware);

            adapterInfo = Manager.Adapters[0]; //Helper class to gain access to information about the display adapter
            LogAdapterInfo();

            SetupPresentParams();

            if ((caps.VertexShaderVersion >= new Version(1, 1)) && (caps.PixelShaderVersion >= new Version(1, 1)))
                this.deviceType = DeviceType.Software;	// Better

            behaviorFlags = CreateFlags.SoftwareVertexProcessing;	// The slowest
            if ((caps.VertexShaderVersion >= new Version(2, 0)) && (caps.PixelShaderVersion >= new Version(2, 0)))
            {
                this.deviceType = DeviceType.Hardware;	// The best performance
                if (caps.DeviceCaps.SupportsHardwareTransformAndLight)
                {
                    behaviorFlags = CreateFlags.HardwareVertexProcessing;
                    if (caps.DeviceCaps.SupportsPureDevice)
                    {
                        behaviorFlags |= CreateFlags.PureDevice; // The best performance
                    }
                }
            }

            //if (!IsDeviceAcceptable(caps, adapterFormat, adapterInfo.CurrentDisplayMode.Format, behaviorFlags, fullScreen))
            //    throw new Exception("No suitable device found!");


        }

        /// <summary>
        /// Setup the Present Parameters
        /// </summary>
        /// <param name="fullscreen">Fullscreen: True/False</param>
        /// <param name="control">The Control to Render to</param>
        private void SetupPresentParams()
        {
            presentParams = new PresentParameters();
            presentParams.BackBufferCount = 1; //Number of backbuffers to create
            presentParams.BackBufferFormat = adapterInfo.CurrentDisplayMode.Format; //The current format of the display device
            presentParams.BackBufferWidth = this.width;
            presentParams.BackBufferHeight = this.height;
            presentParams.SwapEffect = SwapEffect.Discard; //How the backbuffer and the current display will be swapped
            presentParams.AutoDepthStencilFormat = DepthFormat.D16; //16 bits for the depth
            presentParams.EnableAutoDepthStencil = true; //Let direct3d handle the depth buffers for the application
            presentParams.Windowed = true;
            if (isFullScreen)
            {
                presentParams.Windowed = false; //is the application windowed or fullscreen
                presentParams.BackBufferWidth = this.width;
                presentParams.BackBufferHeight = this.height;
                presentParams.PresentationInterval = PresentInterval.Immediate;
            }
        }

        /// <summary>
        /// Check is the device has the minimun characteristics to work
        /// </summary>
        /// <param name="ps">The minimun pixel shader version</param>
        /// <returns>Returns false if the device has not the minimun charaacteristics</returns>
        public bool IsDeviceAcceptable(ManagedUtility.PixelShaderVersion ps)
        {
            if (ps == ManagedUtility.PixelShaderVersion.PS10)
            {
                // Reject any device that doesn't support at least ps1.0
                if (caps.PixelShaderVersion < new Version(1, 0))
                {
                    Trace.Report("This application only works with a device that supports at least shader 1.0");
                    return false;
                }
            }
            else if (ps == ManagedUtility.PixelShaderVersion.PS11)
            {
                // Reject any device that doesn't support at least ps1.1
                if (caps.PixelShaderVersion < new Version(1, 1))
                {
                    Trace.Report("This application only works with a device that supports at least shader 1.1");
                    return false;
                }
            }
            else if (ps == ManagedUtility.PixelShaderVersion.PS12)
            {
                // Reject any device that doesn't support at least ps1.2
                if (caps.PixelShaderVersion < new Version(1, 2))
                {
                    Trace.Report("This application only works with a device that supports at least shader 1.2");
                    return false;
                }
            }
            else if (ps == ManagedUtility.PixelShaderVersion.PS13)
            {
                // Reject any device that doesn't support at least ps1.3
                if (caps.PixelShaderVersion < new Version(1, 3))
                {
                    Trace.Report("This application only works with a device that supports at least shader 1.3");
                    return false;
                }
            }
            else if (ps == ManagedUtility.PixelShaderVersion.PS14)
            {
                // Reject any device that doesn't support at least ps1.4
                if (caps.PixelShaderVersion < new Version(1, 4))
                {
                    Trace.Report("This application only works with a device that supports at least shader 1.4");
                    return false;
                }
            }
            else if (ps == ManagedUtility.PixelShaderVersion.PS20)
            {
                // Reject any device that doesn't support at least ps2.0
                if (caps.PixelShaderVersion < new Version(2, 0))
                {
                    Trace.Report("This application only works with a device that supports at least shader 2.0");
                    return false;
                }
            }
            else if (ps == ManagedUtility.PixelShaderVersion.PS30)
            {
                // Reject any device that doesn't support at least ps3.0
                if (caps.PixelShaderVersion < new Version(3, 0))
                {
                    Trace.Report("This application only works with a device that supports at least shader 3.0");
                    return false;
                }
            }
            else if (ps == ManagedUtility.PixelShaderVersion.PS40)
            {
                // Reject any device that doesn't support at least ps4.0
                // WARNING! Ps4.0 is only suported by Directx 10 and windows vista.
                if (caps.PixelShaderVersion < new Version(4, 0))
                {
                    Trace.Report("This application only works with a device that supports at least shader 4.0");
                    return false;
                }
            }
            return true;
        }

        public void LogAdapterInfo()
        {
            if (adapterInfo == null)
                return;

            Trace.Report("--Adapter Info--");
            Trace.Report("Width: " + adapterInfo.CurrentDisplayMode.Width.ToString() 
                        + "Height: " + adapterInfo.CurrentDisplayMode.Height.ToString());
            Trace.Report("Format: " + adapterInfo.CurrentDisplayMode.Format.ToString());

            Trace.Report(adapterInfo.Information.Description.ToString());
            Trace.Report("PixelShaderVersion: " + caps.PixelShaderVersion.ToString());
            Trace.Report("VertexShaderVersion: " + caps.VertexShaderVersion.ToString());
        }

        /// <summary>
        /// Writes in the log some of the characteristics of the adapter
        /// </summary>
        public void LogDeviceCharacteristics()
        {
            Trace.Report("DriverCaps " + caps.DriverCaps);
            Trace.Report("DeviceType " + caps.DeviceType);
            Trace.Report("MaxActiveLights " + caps.MaxActiveLights);
            Trace.Report("MaxAnisotropy " + caps.MaxAnisotropy);
            Trace.Report("MaxNPatchTessellationLevel " + caps.MaxNPatchTessellationLevel);
            Trace.Report("MaxPixelShader30InstructionSlots " + caps.MaxPixelShader30InstructionSlots);
            Trace.Report("MaxPointSize " + caps.MaxPointSize);
            Trace.Report("MaxPrimitiveCount " + caps.MaxPrimitiveCount);
            Trace.Report("MaxSimultaneousTextures " + caps.MaxSimultaneousTextures);
            Trace.Report("MaxStreams " + caps.MaxStreams);
            Trace.Report("MaxStreamStride " + caps.MaxStreamStride);
            Trace.Report("MaxTextureAspectRatio " + caps.MaxTextureAspectRatio);
            Trace.Report("MaxTextureBlendStages " + caps.MaxTextureBlendStages);
            Trace.Report("MaxTextureHeight " + caps.MaxTextureHeight);
            Trace.Report("MaxTextureRepeat " + caps.MaxTextureRepeat);
            Trace.Report("MaxTextureWidth " + caps.MaxTextureWidth);
            Trace.Report("MaxUserClipPlanes " + caps.MaxUserClipPlanes);
            Trace.Report("MaxVertexBlendMatrices " + caps.MaxVertexBlendMatrices);
            Trace.Report("MaxVertexBlendMatrixIndex " + caps.MaxVertexBlendMatrixIndex);
            Trace.Report("MaxVertexIndex " + caps.MaxVertexIndex);
            Trace.Report("MaxVertexShader30InstructionSlots " + caps.MaxVertexShader30InstructionSlots);
            Trace.Report("MaxVertexShaderConst " + caps.MaxVertexShaderConst);
            Trace.Report("MaxVertexW " + caps.MaxVertexW);
            Trace.Report("MaxVolumeExtent " + caps.MaxVolumeExtent);
            Trace.Report("NumberSimultaneousRts " + caps.NumberSimultaneousRts);
            Trace.Report("PixelShaderVersion " + caps.PixelShaderVersion);
            Trace.Report("VertexShaderVersion " + caps.VertexShaderVersion);
        }
    }

    #region Helper Utility Class
    /// <summary>
    /// Helper methods
    /// </summary>
    public static class ManagedUtility
    {
        /// <summary>
        /// Gets the number of ColorChanelBits from a format
        /// </summary>
        public static uint GetColorChannelBits(Format format)
        {
            switch (format)
            {
                case Format.R8G8B8:
                case Format.A8R8G8B8:
                case Format.X8R8G8B8:
                    return 8;
                case Format.R5G6B5:
                case Format.X1R5G5B5:
                case Format.A1R5G5B5:
                    return 5;
                case Format.A4R4G4B4:
                case Format.X4R4G4B4:
                    return 4;
                case Format.R3G3B2:
                case Format.A8R3G3B2:
                    return 2;
                case Format.A2B10G10R10:
                case Format.A2R10G10B10:
                    return 10;
                default:
                    return 0;
            }
        }

        /// <summary>
        /// Gets the number of alpha channel bits 
        /// </summary>
        public static uint GetAlphaChannelBits(Format format)
        {
            switch (format)
            {
                case Format.X8R8G8B8:
                case Format.R8G8B8:
                case Format.R5G6B5:
                case Format.X1R5G5B5:
                case Format.R3G3B2:
                case Format.X4R4G4B4:
                    return 0;
                case Format.A8R3G3B2:
                case Format.A8R8G8B8:
                    return 8;
                case Format.A1R5G5B5:
                    return 1;
                case Format.A4R4G4B4:
                    return 4;
                case Format.A2B10G10R10:
                case Format.A2R10G10B10:
                    return 2;
                default:
                    return 0;
            }
        }

        /// <summary>
        /// Gets the number of depth bits
        /// </summary>
        public static uint GetDepthBits(DepthFormat format)
        {
            switch (format)
            {
                case DepthFormat.D16:
                case DepthFormat.D16Lockable:
                    return 16;

                case DepthFormat.D15S1:
                    return 15;

                case DepthFormat.D24X8:
                case DepthFormat.D24S8:
                case DepthFormat.D24X4S4:
                case DepthFormat.D24SingleS8:
                    return 24;

                case DepthFormat.D32:
                case DepthFormat.D32SingleLockable:
                    return 32;
                default:
                    return 0;
            }
        }

        /// <summary>
        /// Gets the number of stencil bits
        /// </summary>
        public static uint GetStencilBits(DepthFormat format)
        {
            switch (format)
            {
                case DepthFormat.D16:
                case DepthFormat.D16Lockable:
                case DepthFormat.D24X8:
                case DepthFormat.D32:
                case DepthFormat.D32SingleLockable:
                    return 0;

                case DepthFormat.D15S1:
                    return 1;

                case DepthFormat.D24X4S4:
                    return 4;

                case DepthFormat.D24SingleS8:
                case DepthFormat.D24S8:
                    return 8;

                default:
                    return 0;
            }
        }

        /// <summary>
        /// Pixel shader versions
        /// </summary>
        public enum PixelShaderVersion
        {
            PS10,
            PS11,
            PS12,
            PS13,
            PS14,
            PS20,
            PS30,
            PS40
        }

        /// <summary>
        /// Vertex shader versions
        /// </summary>
        public enum VertexShaderVersion
        {
            VS10,
            VS11,
            VS20,
            VS30,
            VS40
        }

        /// <summary>
        /// Array with the allowed format colors
        /// </summary>
        public static readonly Format[] allowedFormats = new Format[] 
        {
            Format.X8R8G8B8, 
            Format.X1R5G5B5,
            Format.R5G6B5,
            Format.A2R10G10B10 
        };

        /// <summary>
        /// Array with the allowed backbuffer formats
        /// </summary>
        public static readonly Format[] backbufferFormatsArray = new Format[] 
        {
            Format.A8R8G8B8,
            Format.X8R8G8B8, 
            Format.A1R5G5B5,
            Format.X1R5G5B5,
            Format.R5G6B5,
            Format.A2R10G10B10 
        };

        /// <summary>
        /// Array with the different devices supported by directx
        /// </summary>
        public static readonly DeviceType[] deviceTypeArray = new DeviceType[] 
        {
            DeviceType.Hardware,
            DeviceType.Software,
            DeviceType.Reference 
        };

        /// <summary>
        /// Array with the different versions of pixel shaders available
        /// </summary>
        public static readonly PixelShaderVersion[] pixelShaderTypeArray = new PixelShaderVersion[] {
            PixelShaderVersion.PS10,
            PixelShaderVersion.PS11,
            PixelShaderVersion.PS12,
            PixelShaderVersion.PS13,
            PixelShaderVersion.PS14,
            PixelShaderVersion.PS20,
            PixelShaderVersion.PS30,
            PixelShaderVersion.PS40 
        };

        /// <summary>
        /// Array with the different versions of vertex shaders available
        /// </summary>
        public static readonly VertexShaderVersion[] vertexShaderTypeArray = new VertexShaderVersion[] {
            VertexShaderVersion.VS10,
            VertexShaderVersion.VS20,
            VertexShaderVersion.VS30,
            VertexShaderVersion.VS40 
        };
    #endregion
    }
}
